/*
Brick And Blast, a crossplatform breakout game.
 Copyright (C) 2008-2010 Julien CLEMENT (andematunum@gmail.com)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 long with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/**
* file   Level.cpp
* date   Mar 22 déc 2009 19:14:01 CET
* brief  TODO
*/

#include "Level.h"
#include "Ball.h"
#include "Bat.h"
#include "Bumper.h"
#include "Edge.h"
#include "Brick.h"
#include "Collider.h"
#include "ObjectGrid.h"
#include "Settings.h"
#include "SDL_image.h"

#include <iostream>
#include <set>
using namespace std;

/**
  * STATIC PART
  */
SDL_TextureID Level::tboard_   = 0;
SDL_TextureID Level::tedge_    = 0;
SDL_TextureID Level::tbumper_  = 0;
SDL_TextureID Level::tball_    = 0;
SDL_Surface* Level::sbricks_  = NULL;
//SDL_Surface* Level::sbat_     = NULL;
SDL_Surface* Level::soptsel_  = NULL;
SDL_Surface* Level::soptblocked_ = NULL;
SDL_TextureID Level::tbat_left_ = 0;
SDL_TextureID Level::tbat_right_ = 0;
SDL_TextureID Level::tbat_chunk_ = 0;


void
Level::
load_surface (SDL_Surface*& surf, const char* file)
throw(BnbException) {
  surf = IMG_Load(file);
  if (surf == NULL) {
    cerr << "*** ERROR: load_surface(): cannot load image: \""
         << file << "\"" << endl;
    throw(1);
  }
}

void
Level::
load_texture (SDL_TextureID& texid, const char* file)
throw(BnbException) {
  SDL_Surface* surface = NULL;
  load_surface(surface,file);
  texid = SDL_CreateTextureFromSurface(0, surface);
  if (!texid) {
      cerr << "*** ERROR: load_texture(): cannot create texture from image: \""
           << file << "\"" << endl;
    SDL_FreeSurface(surface);
    throw(1);
  }
  SDL_FreeSurface(surface);
}

SDL_TextureID
Level::
get_tile (SDL_Surface* tileset, int n) {
  SDL_Surface* tile = SDL_CreateRGBSurface(
    tileset->flags,
    32, 16, tileset->format->BitsPerPixel,
    0, 0, 0, 0
  );
  // $$ This prevents blending effects ...
  /*
    tileset->format->Rmask,
    tileset->format->Gmask,
    tileset->format->Bmask,
    tileset->format->Amask
  );*/
  SDL_Rect src = { n*32, 0, 32, 16 };
  SDL_Rect dst = { 0, 0, 32, 16 };
  SDL_UpperBlit(tileset,&src,tile,&dst);
  Uint32 colorkey;
  SDL_GetColorKey(tileset, &colorkey);
  SDL_SetColorKey(tile, SDL_SRCCOLORKEY, colorkey);
  
  SDL_TextureID texture = SDL_CreateTextureFromSurface(0, tile);
  SDL_SetTextureScaleMode(texture, SDL_TEXTURESCALEMODE_BEST);
  SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_NONE|SDL_TEXTUREMODULATE_ALPHA);

  return texture;
}

void
Level::
load_resources () throw(BnbException) {
  try {
    load_texture(tboard_, "data/gfx/board.png");
    load_texture(tedge_, "data/gfx/edge.png");
    load_texture(tbumper_, "data/gfx/bumper.png");
    load_texture(tball_, "data/gfx/ball.png");
    load_surface(sbricks_, "data/gfx/bricks.png");
    //load_surface(sbat_, "data/gfx/bat.png");
    load_surface(soptsel_, "data/gfx/optsel.png");
    load_surface(soptblocked_, "data/gfx/optblocked.png");
    load_texture (tbat_left_, "data/gfx/bat_left.png");
    load_texture (tbat_right_, "data/gfx/bat_right.png");
    load_texture (tbat_chunk_, "data/gfx/bat_chunk.png");
  }
  catch(BnbException& e) {
    /** If something failed during the loading of
      * at least one resource, free everything which
      * was previously loaded. */
    free_resources();
    /** Re-throw the exception to the client code. */
    throw(e);
  }
}

void
Level::
free_resources () {
  SDL_TextureID texids[7] = {
    tboard_,
    tedge_,
    tbumper_, 
    tball_,
    tbat_left_,
    tbat_right_,
    tbat_chunk_
  };
  SDL_Surface* surfs[3] = {
    sbricks_,
//    sbat_,
    soptsel_,
    soptblocked_ 
  };
  for (int k = 0; k < 7; k++) {
    SDL_TextureID texid = texids[k];
    if (texid) {
      SDL_DestroyTexture(texid);
    }
    if (k < 3) {
      SDL_FreeSurface(surfs[k]);
    }
  }
}

/**
  * INSTANCE PART
  */
Level::
Level (const char* file) {
  //loadFromFile(file);
  init();
}

Level::
~Level () {
}

void
Level::
init () {
  SDL_SetRenderDrawColor (0, 0, 0, 0);
  SDL_RenderRect(NULL);
  grid_.set_margin(16, 16);
}

void
Level::
load_from_file (const char* file) throw(BnbException) {
  //LevelIO::load(level_data_, file);
}

Ball*
Level::
create_ball (SDL_Point& xy, double angle, double velocity) {
  if (angle == 10.) {
    angle = (double)random() / (double)RAND_MAX * 2. * M_PI;
  }
  if (velocity == -1.) {
    velocity = 1. + (double)random() / (double)RAND_MAX * 4.;
  }
  Ball* ball = new Ball(
    xy, Level::get_ball(), velocity
  );
  if (angle != 10.) {
    ball->set_angle(angle);
  }
  else {
    ball->set_random_direction();
  }
  balls_.push_back(ball);
  create_object(ball);
  return ball;
}

void
Level::
destroy_ball (Ball* ball) {
  destroy_object(ball);
  balls_.remove(ball);
}

Bat*
Level::
create_bat (int nchunks) {
  Bat* bat = new Bat(nchunks);
  set<GameObject*> parts;
  bat->get_parts(parts);
  for (set<GameObject*>::iterator itr = parts.begin();
       itr != parts.end(); itr++) {
    create_object(*itr);
  }
  bat_ = bat;
  return bat;
}

void
Level::
destroy_bat (Bat* bat) {
  set<GameObject*> parts;
  bat->get_parts(parts);
  for (set<GameObject*>::iterator itr = parts.begin();
      itr != parts.end(); itr++) {
    destroy_object(*itr);
  }
}

Bumper*
Level::
create_bumper (SDL_Point& xy) {
  Bumper* bumper = new Bumper(xy, Level::get_bumper());
  bumpers_.push_back(bumper);
  create_object(bumper);
  return bumper;
}

void
Level::
destroy_bumper (Bumper* bumper) {
  destroy_object(bumper);
  bumpers_.remove(bumper);
}

Brick*
Level::
create_brick (Brick::BrickType type, SDL_Point& xy) {
  Brick* brick = new Brick(type, Brick::BRICK_NONE, xy);
  bricks_.push_back(brick);
  create_object(brick);
  return brick;
}

void
Level::
destroy_brick (Brick* brick) {
  destroy_object(brick);
  bricks_.remove(brick);
}

void
Level::
create_edges (int size) {
  SDL_Point posleft = {16, 648};
  SDL_Point posright = {992, 648};
  //SDL_Point posleft = {16, 600};
  //SDL_Point posright = {992, 600};

  for (int k = 0; k < size; k++, posleft.x+=16, posright.x-=16) {
    Edge* edge = NULL;
    if (k == size-1) {
      edge = new Edge(posleft, Level::get_edge(), Edge::EDGE_CORNER_LEFT);
    }
    else {
      edge = new Edge(posleft, Level::get_edge());
    }
    edges_.push_back(edge);
    create_object(edge);
    if (k == size-1) {
      edge = new Edge(posright, Level::get_edge(), Edge::EDGE_CORNER_RIGHT);
    }
    else {
      edge = new Edge(posright, Level::get_edge());
    }
    edges_.push_back(edge);
    create_object(edge);
  }
}

void
Level::
destroy_edges () {
  for (list<Edge*>::iterator itr = edges_.begin();
       itr != edges_.end(); itr++) {
    destroy_object(*itr);
    edges_.remove(*itr);
  }
}

void
Level::
run () {
  SDL_Event evt;
  int done = 0;
  SDL_RenderCopy(Level::get_board(),NULL,NULL);

  SDL_ShowCursor(0);

  Uint32 time_start = 0;
  Uint32 time_end   = 0;

  for (;!done;) {
    // Update all the objects and clear the screen
    // after some delay when at least one object changed.
    SDL_RenderCopy(Level::get_board(),NULL,NULL);
    update_all();

    while(SDL_PollEvent(&evt) > 0) {
      if ((evt.type == SDL_QUIT) ||
          ((evt.type == SDL_KEYDOWN) && (evt.key.keysym.sym == SDLK_ESCAPE))) {
          done = 1;
          break;
      }
      else if (evt.type == SDL_KEYDOWN) {
        if (evt.key.keysym.sym == SDLK_SPACE) {
          for (list<Ball*>::iterator itr = balls_.begin();
               itr != balls_.end(); itr++) {
             (*itr)->set_random_direction();
          }
        }
        else if (evt.key.keysym.sym == SDLK_KP_PLUS) {
          int n = bat_->get_nchunks();
          if (n < BAT_MAX_CHUNKS) {
            destroy_bat(bat_);
            create_bat(n+1);
          }
        }
        else if (evt.key.keysym.sym == SDLK_KP_MINUS) {
          int n = bat_->get_nchunks();
          if (n > 1) {
            destroy_bat(bat_);
            create_bat(n-1);
          }
        }
      }
      else if (evt.type == SDL_MOUSEMOTION) {
        mouse_x_ = evt.motion.x;
      }
    }
   // Temporary
   if (balls_.size() == 0) {
    done = 1;
    break;
   }
   if (bricks_.size() == 0) {
    done = 1;
    break;
   }

   /*time_end = SDL_GetTicks();
   if (time_start > 0) {
    Uint32 diff_time = time_end - time_start;
    if (diff_time < Settings::get_frame_duration()) {
      SDL_Delay(Settings::get_frame_duration() - diff_time);
    }
   }*/
   SDL_RenderPresent();
   // Yes, the real time start is not equal to the previous
   // time end, beware !
   //time_start = SDL_GetTicks();
  }
}

void
Level::
update_all () {
  // Warning: edges must be updated
  // *before* the bat so that the bat
  // is above them.
  update_edges();
  update_balls();
  update_bat();
  update_bricks();
  update_bumpers();
}

void
Level::
update_balls () {

  set< Cell* > cells;

  for (list<Ball*>::iterator itr = balls_.begin();
       itr != balls_.end(); itr++)
  {
    Ball* ball = *itr;

    if (!ball->alive()) {
     destroy_ball(ball);
     continue;
    }

    ball->calculate_next_pixel_position();
    if (ball->pixel_position_changed()) {
      set<GameObject*> neighbors;
      grid_.get_neighborhood(neighbors, cells, ball);
      // Builds up the 'collisions_' list
      detect_neighborhood_collisions(*ball, neighbors);
      ball->handle_game_area_collisions();
    }
  }

  if (collisions_.size() > 0) {
    handle_collisions();
  }

  for (list<Ball*>::iterator itr = balls_.begin();
       itr != balls_.end(); itr++)
  {
    Ball* ball = *itr;
    ball->update_direction();
    // The next pixel position may differ from the one
    // calculated before, as a result of one or more collisions.
    ball->calculate_next_pixel_position();
    if (ball->pixel_position_changed()) {
      grid_.update_object(ball, cells);
      ball->update_pixel_position();
    }
    ball->update();
    ball->blit();
  }
}

void
Level::
update_bat () {
  if (bat_ != NULL) {
    set<GameObject*> parts;
    bat_->get_parts(parts);
    for (set<GameObject*>::iterator itr = parts.begin();
        itr != parts.end(); itr++) {
      grid_.remove_object(*itr);
    }
    bat_->set_x_position(mouse_x_ - bat_->get_rect()->w/2);
    bat_->blit();
    for (set<GameObject*>::iterator itr = parts.begin();
        itr != parts.end(); itr++) {
      grid_.add_object(*itr);
    }
  }
}

void
Level::
update_bumpers () {
  for (list<Bumper*>::iterator itr = bumpers_.begin();
       itr != bumpers_.end(); itr++) {
     Bumper* bumper = *itr;
     bumper->update();
     bumper->blit();
  }  
}

void
Level::
update_bricks () {
  for (list<Brick*>::iterator itr = bricks_.begin();
       itr != bricks_.end(); itr++) {
    Brick* brick = *itr;
    if (!brick->alive()) {
      destroy_brick(brick);
    }
    else {
      brick->update();
      brick->blit();
    }
  }
}

void
Level::
update_edges () {
  for (list<Edge*>::iterator itr = edges_.begin();
       itr != edges_.end(); itr++) {
     Edge* edge = *itr;
     edge->update();
     edge->blit();
  }  
}

void
Level::
detect_neighborhood_collisions (GameObject& obj, set<GameObject*>& neighborhood) {
  for (set<GameObject*>::iterator itr = neighborhood.begin();
      itr != neighborhood.end(); itr++) {

    GameObject* other = *itr;
    Collision collision(&obj, other, NULL);
    if (collisions_.find(&collision) != collisions_.end()) {
      continue;
    }
    ColliderData* data = new ColliderData;
    other->calculate_next_pixel_position();
    if (obj.check_shape_collision(other, data)) {
      data->from_object = &obj;
      pair<set<Collision*,Collision>::iterator,bool> p = collisions_.insert(new Collision(&obj, other, data));
      if (p.second == false) {
        cerr << "*** ERROR: Couldn't insert a new collision !" << endl;
      }
    }
    else {
      delete data;
    }
  }
}

void
Level::
handle_collisions () {
  for (set<Collision*,Collision>::iterator itr = collisions_.begin();
       itr != collisions_.end(); itr++) {
   Collision* collision = *itr;
   collision->handle();
  }
  collisions_.clear();
}

#ifdef TEST_1
/**
  * TEST 1: Load & free static resources
  */
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 1",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          320, 240, SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  try {
    Level::load_resources();
    {
      Level level("");
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif


#ifdef TEST_2
/** TEST 2: Create a bouncing ball */
#include "Ball.h"
#define NBALLS (1)
#define ANGLE_NOISE (0)//(M_PI / 128.)
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 2",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));

  try {
    Level::load_resources();
    {
      Level level("");
      SDL_Event evt;
      int done = 0;
      SDL_Point pos = {(1024-8)/2, (768-8)/2};
      // Angle = 10.  crashes
      Ball balls[NBALLS];
      //Ball ball(pos, Level::get_ball(), M_PI/2.+M_PI/4., 1.);
      for (int k = 0;k < NBALLS; k++) {
        balls[k] = Ball(
          pos,
          Level::get_ball(),
          //(double)random() / (double)RAND_MAX * 2. * M_PI,
          //(double)random() / (double)RAND_MAX * 5. + 1.
          0.1,
          3.
          //M_PI/2.+M_PI/4., 5.
        );
      }
      // Clear the screen
      SDL_SetRenderDrawColor (0x00,0x00,0x22,0x00);
      SDL_RenderRect(NULL);

      Uint32 t_start, t_end;
      int npixels = 0;

      t_start = SDL_GetTicks();

      for (;!done;) {
      #ifdef FLIP
        SDL_RenderRect(NULL);
        //SDL_RenderCopy(Level::get_board(), NULL, NULL);
      #endif
        while (SDL_PollEvent(&evt) > 0) {
          if ((evt.type == SDL_QUIT) ||
          (evt.type == SDL_KEYDOWN) && (evt.key.keysym.sym == SDLK_ESCAPE)) {
            done = 1;
            break;
          }
        }
        for (int k = 0; k < NBALLS; k++) {
          Ball& ball = balls[k];
          SDL_Point next;
          if (ball.get_next_position(next)) {
            bool collide = false;
            if (((next.x-8) < 0) || ((next.x+8) >= 1024)) {
              ball.collide_horizontal();
              ball.set_angle(ball.get_angle() +
                -ANGLE_NOISE + 2.0 * ANGLE_NOISE * ((double)random() / (double)RAND_MAX));
              collide = true;
            }
            if (((next.y-8) < 0) || ((next.y+8) >= 768)) {
              ball.collide_vertical();
              ball.set_angle(ball.get_angle() +
                -ANGLE_NOISE + 2.0 * ANGLE_NOISE * ((double)random() / (double)RAND_MAX));
              collide = true;
            }
            if (collide) {
              ball.get_next_position(next);
            }
            ball.move_to(next);
            //ball.print_grid_position();
            npixels++;
          }
          ball.update();
        }
        t_end = SDL_GetTicks();
        if (t_end - t_start > 1000) {
          cout << "*** INFO: Estimated speed: " << npixels << " pixels per second." << endl;
          t_start = t_end;
          npixels = 0;
        }
        SDL_RenderPresent();
        SDL_Delay(10);
      }
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

#ifdef TEST_3
/** TEST 3: Create multiple bouncing balls colliding each other */
#include "Ball.h"
#define NBALLS (200)
#define ANGLE_NOISE (M_PI / 128.)
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 3",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));

  try {
    Level::load_resources();
    {
      Level level("");
      SDL_Point xy = { 25, 25 };
      for (int k = 0; k < 8; k++, xy.y += 75) {
        xy.x = 25;
        for (int l = 0; l < 8; l++, xy.x += 75) {
          level.create_ball(xy, 10., 2.);
        }
      }
      level.run();
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

#ifdef TEST_4
/** TEST 4: Create a bouncing ball and a bat */
#include "Ball.h"
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 4",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));

  try {
    Level::load_resources();
    {
      Level level("");
      level.create_bat(1);
      SDL_Point xy = {504, 300};
      for (xy.x = 32; xy.x < 900; xy.x += 34) {
        level.create_ball(xy, 10., 3.);
      }
      level.run();
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

#ifdef TEST_5
/** TEST 5: Add bumpers */
#include "Ball.h"
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 5",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));


  try {
    Level::load_resources();
    {
      Level level("");
      Bat* b = level.create_bat(1);
      SDL_WarpMouseInWindow(win,
        b->get_rect()->x + 0.5*(double)b->get_rect()->w,
        640);
      SDL_Point xy = {504, 400};
      level.create_bumper(xy);
      xy.x = 200;
      xy.y = 300;
      level.create_bumper(xy);
      xy.x = 800;
      level.create_bumper(xy);
      xy.y = 400;
      for (xy.x = 32; xy.x < 900; xy.x += 32) {
        level.create_ball(xy, 10., 3.);
      }
      level.run();
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

#ifdef TEST_6
/** TEST 6: Add edges */
#include "Ball.h"
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 6",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));


  try {
    Level::load_resources();
    {
      Level level("");
      level.create_edges(8);
      Bat* b = level.create_bat(1);
      SDL_WarpMouseInWindow(win,
        b->get_rect()->x + 0.5*(double)b->get_rect()->w,
        640);
      /*SDL_Point xy = {32, 96};
      for (xy.x = 32; xy.x <= 992; xy.x+=32) {
        level.create_bumper(xy);
      }
      xy.y = 48;*/
      SDL_Point xy = {504, 400};
      level.create_bumper(xy);
      xy.x = 200;
      xy.y = 300;
      level.create_bumper(xy);
      xy.x = 800;
      level.create_bumper(xy);
      xy.y = 400;
      for (xy.x = 32; xy.x < 900; xy.x += 128) {
        level.create_ball(xy, 10., 3);
      }
      level.run();
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

#ifdef TEST_7
/** TEST 6: Add bricks */
#include "Ball.h"
int main (int argc, char** argv) {
  SDL_WindowID win = SDL_CreateWindow("TestLevel 7",
                          SDL_WINDOWPOS_UNDEFINED,
                          SDL_WINDOWPOS_UNDEFINED,
                          1024, 768, /*SDL_WINDOW_FULLSCREEN|*/SDL_WINDOW_SHOWN);
  if (!win) {
    fprintf(stderr,"*** ERROR: Couldn't create an SDL Window.\n");
    return EXIT_FAILURE;
  }

  srandom(time(0));

  try {
    Level::load_resources();
    {
      Level level("");
      level.create_edges(30);
      Bat* b = level.create_bat(5);
      SDL_WarpMouseInWindow(win,
        b->get_rect()->x + 0.5*(double)b->get_rect()->w,
        640);
// Not working ...
//      SDL_SelectMouse(1);
//      SDL_ShowCursor(0);
      // BUMPER SPECIAL TEST
      /*SDL_Point xy = {32, 96};
      for (xy.x = 32; xy.x <= 992; xy.x+=32) {
        level.create_bumper(xy);
      }
      xy.y = 48;*/
      SDL_Point xy = {504, 400};
      level.create_bumper(xy);
      xy.x = 200;
      xy.y = 300;
      level.create_bumper(xy);
      xy.x = 800;
      level.create_bumper(xy);

      for (xy.y = 100; xy.y < 368; xy.y += 32) {
        for (xy.x = ((xy.y-100) % 64) ? (232) : (264); xy.x < 770; xy.x += 128) {
          double randtype_double = (double)random() / (double)RAND_MAX * (double)0x05;
          Brick::BrickType randtype =
            static_cast<Brick::BrickType>(randtype_double);
          level.create_brick(randtype, xy);
        }
      }

      xy.y = 400;
      for (xy.x = 32; xy.x < 900; xy.x += 76) {
        level.create_ball(xy, 10., 1);
      }
      level.run();
    }
    Level::free_resources();
  }
  catch(BnbException e) {
    fprintf(stderr,"*** ERROR: Test FAILED.\n");
    return EXIT_FAILURE;
  }

  fprintf(stdout,"*** INFO: Test PASSED.\n");
  return EXIT_SUCCESS;
}
#endif

